#include <Arduino.h>
#include "button.h"

// 按键去抖状态定义
#define BUTTON_ST_UP   0
#define BUTTON_ST_DEB  1
#define BUTTON_ST_DOWN 2

#define BUTTON_EVENT_MAX_RECORD 1 // 最大记录数，表示可以记录多少次按键

static int button_state[MAX_BUTTON_NUM]; // 每个按键的状态
static unsigned long button_deb_start_tm[MAX_BUTTON_NUM]; // 每个按键的去抖时间戳

static int btn_pin_list[MAX_BUTTON_NUM];

static int button_event_cnt[MAX_BUTTON_NUM]; // 每个按键的事件计数，按键增加1，读走减少1。

static int button_number = 0; // 按键个数

/**
 * @brief 初始化按键
 * 
 * @note 在setup中调用
 * 
 * @param btnNumber [in] number of button
 * @param btnPinList [in] list of button PIN
 * @param btnList [in] list of button value
 */
void button_init(int btnNumber, 
                 int * btnPinList)
{
    int i = 0;

    // 参数检查
    if (btnNumber < 1 || 
        btnNumber > MAX_BUTTON_NUM ||
        btnPinList == NULL)
    {
        // 参数错误
        return;
    }

    // 将参数保存到本地变量
    memcpy(btn_pin_list, btnPinList, sizeof(btnPinList[0]) * btnNumber);

    // 初始化引脚
    for (i = 0; i < btnNumber; i++)
    {
        pinMode(btn_pin_list[i], INPUT_PULLUP);
    }

    // 初始化按键状态
    for (i = 0; i < MAX_BUTTON_NUM; i++)
    {
        button_event_cnt[i] = 0;
        button_state[i] = BUTTON_ST_UP;
        button_deb_start_tm[i] = 0;
    }

    button_number = btnNumber;
}

// Call in each loop
// 在主loop中调用此接口
int button_polling(void)
{
    int i = 0;
    for (i = 0; i < button_number; i++)
    {
        switch (button_state[i])
        {
            case BUTTON_ST_UP:
            if (BUTTON_DOWN_LEVEL == digitalRead(btn_pin_list[i]))
            {
                // 检测到按下，开始去抖
                button_deb_start_tm[i] = millis();
                button_state[i] = BUTTON_ST_DEB;
            }
            break;

            case BUTTON_ST_DEB:
            if (BUTTON_DOWN_LEVEL == digitalRead(btn_pin_list[i]))
            {
                // 继续检测到按下，检测去抖时间
                if ((button_deb_start_tm[i] + BUTTON_DEBOUNCE_MS) < millis())
                {
                    // 超过去抖时间了，视为按钮按下
                    if (button_event_cnt[i] < BUTTON_EVENT_MAX_RECORD)
                    {
                        button_event_cnt[i]++; // 记录一次按键事件
                    }
                    else
                    {
                        // 超过记录数量限制，直接丢弃，直到按键被读走
                        ;
                    }

                    // 已上报按键事件
                    button_state[i] = BUTTON_ST_DOWN;
                }
                else
                {
                    // 没有超过去抖时间，继续去抖
                    ;
                }
            }
            else
            {
                // 按键弹起了
                button_state[i] = BUTTON_ST_UP;
            }
            break;

            case BUTTON_ST_DOWN:
            if (BUTTON_DOWN_LEVEL == digitalRead(btn_pin_list[i]))
            {
                // 已经报了按键之后，还继续按下
                // 简单处理: 直接忽略
                ;
            }
            else
            {
                // 等到按键弹起
                button_state[i] = BUTTON_ST_UP;
            }
            break;
        }
    }
}

// 检查指定的按键是否有按下按键，但不读出按键
// 按键掩码示例: BUTTON_BIT_MASK(0)|BUTTON_BIT_MASK(1)
// 所有按键的掩码: BUTTON_ALL_MASK
// 返回:
//   0 - 没有按键
// 非0 - 有按键的位掩码
int is_button_hit(unsigned long btnBitMask)
{
    int ret = 0;
    int i = 0;
    for (i = 0; i < MAX_BUTTON_NUM; i++)
    {
        if (btnBitMask & BUTTON_BIT_MASK(i))
        {
            if (button_event_cnt[i] > 0)
            {
                ret |= BUTTON_BIT_MASK(i);
            }
        }
    }

    return ret;
}

// 检查是否有任何按键按下
// 返回:
//   0 - 没有任何按键
// 非0 - 有按键按下
int is_any_button_hit(void)
{
    return  is_button_hit(BUTTON_ALL_MASK);
}

// 读出指定编号的按键
//  btnId: 按键编号，0~5
//   有按键，返回1
//   没有按键，返回0
int button_get(int btnId)
{
    int ret = 0;
    if (btnId >= 0 && btnId < MAX_BUTTON_NUM)
    {
        if (button_event_cnt[btnId] > 0)
        {
            ret = 1;
            button_event_cnt[btnId]--;
        }
    }

    return ret;
}
